/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/* 
 * Copyright (c) 2009 City University of Hong Kong
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License m_version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Author: Wilson Thong (wilsonwk@ee.cityu.edu.hk) 
 */

#include <vector>

#include "ns3/node.h"
#include "ns3/nstime.h"
#include "ns3/application-container.h"
#include "ns3/point-to-point-helper.h"
#include "ns3/test.h"
#include "ns3/node-container.h"
#include "ns3/data-rate.h"
#include "ns3/point-to-point-grid-helper.h"
#include "ns3/stats-module.h"
#include "ns3/point-to-point-grid-helper.h"
#include "ns3/ipv4-route.h"
#include "ns3/basic-data-calculators.h"
#include "ns3/time-data-calculators.h"
#include "ns3/packet-data-calculators.h"
#include "ns3/Histogram.h"

namespace ns3 {
namespace eecityu {

using namespace std;

class HistogramCalculator;

/**
 * \brief Get the remote node of a net device from a local node
 *
 * \param local A local node
 * \param devId An index of a net device on the local node
 *
 * \return The remote node
 */
Ptr<Node>
GetRemoteNode (Ptr<Node> local, uint32_t devId);

/**
 * \brief Get all neighboring nodes of a root nodes at some hop distance away from the root
 *
 * \param root The root node of which neighboring nodes are to be found
 * \param hop The hop distances of all neighbors away from the root
 * 
 * \return A collection of all neighboring nodes or root
 */
NodeContainer
GetNeighborsAtHop (Ptr<Node> root, uint32_t hop);

/**
 * \brief Randomly select two nodes with some hop distance
 *
 * \param hopDistance The distance in hop counts between the two nodes
 * randomly selected
 * \return The two nodes randomly selected. If no such node pair exists,
 * return a pair object with two NULL pointers
 */
pair<Ptr<Node>, Ptr<Node> >
GetTwoNodesAtRandom (uint32_t hopDistance);

/**
 * \brief Write to a file describing the topology and statistics of each channel
 *
 * Write to a file describing the topology and statistics of each channel. 
 * If specified, statistics from the two directional links are merged if 
 * they are connected between the same two nodes.
 *
 * \param filename File name of the graph definition file
 * \param duration The simulation duration over which traffic is being sent. This
 * time preiod is used to calculate average utilization of links
 * \param merging Set to true to merge the statistics of the two directional links
 * into one unidirectional link, if they are on the same two nodes
 */
void
WriteGraph (string filename, Time duration, bool merging = true);

/**
 * \brief Install Poisson traffic between two nodes
 *
 * \param src The source node from which Poisson traffic is generated
 * \param dst The destination node to which the Poisson traffic goes
 * \param dataRate The averge data rate of the Poisson traffic to be installed
 * \param numPkt Number of packets to generate
 * \param pktSize The packet size in bytes for the Poisson traffic stream
 *
 * \return Container of all sourcing and sinking applications. The souring
 * and sinking applications are organized in the container as follow,
 * <sourcing-0, sinking-0, sourcing-1, sinking-1, ...>
 */
ApplicationContainer
InstallTrafficFlow (Ptr<Node> src, Ptr<Node> dst, DataRate dataRate, uint32_t numPkt = 0, uint32_t pktSize = 512);

/**
 * \brief Randomly allocates Poisson traffic flow to the network
 *
 * \param numFlows Number of Poisson traffic flow to be installed
 * \param flowLength The flow distance from measured from source node to destination node
 * \param flowRate the average rate of each Poisson traffic flow
 * \param pktSize Packet size for each Poisson traffic flow
 * 
 * \return Container of all sourcing and sinking applications. The souring
 * and sinking applications are organized in the container as follow,
 * <sourcing-0, sinking-0, sourcing-1, sinking-1, ...>
 */
ApplicationContainer
InstallRandomTraffic (uint32_t numFlows, uint32_t flowLength, DataRate flowRate, uint32_t pktSize);

/**
 * \brief Allocates four traffic flows at all four directions on each node
 *
 * \param grid A grid topology over which traffic flows are allocated
 * \param nRows Number of rows of the grid topology
 * \param nCols Number of columns of the grid topology
 * \param flowLength The length of each flow to be allocated
 * \param flowRate The data rate of each flow
 * \param numPkt Number of packets to generate
 * \param pktSize The packet size generated by each flow
 * 
 * \return Container of all sourcing and sinking applications. The souring
 * and sinking applications are organized in the container as follow,
 * <sourcing-0, sinking-0, sourcing-1, sinking-1, ...>
 */
ApplicationContainer
InstallGridTraffic (PointToPointGridHelper grid, uint32_t nRows, uint32_t nCols, 
                    uint32_t flowLength, DataRate flowRate, uint32_t numPkt = 0, uint32_t pktSize = 512);

/**
 * \brief Allocates Poisson traffic on all links when some ratio of link utilization
 *
 * It allocates Poisson traffic to all links in both direction in order to form 
 * background traffic. The traffic goes only one hop, so they are all statistically
 * indpendent. 
 *
 * \param linkUtil Link utilization. It is value from 0.0 to 1.0, and indicates
 * the fraction of the link's bandwidth used by background traffic
 * \param pktSize Packet size used during traffic generation
 * \param port Destination port number for the Poisson traffic
 *
 * \return Container of all sourcing and sinking applications. The souring
 * and sinking applications are organized in the container as follow,
 * <sourcing-0, sinking-0, sourcing-1, sinking-1, ...>
 */
ApplicationContainer
InstallBackgroundTraffic (double linkUtil, uint32_t pktSize);

/**
 * \brief Close the grid edges after PointToPointGridHelper created the grid topology
 *
 * \param nRows Number of rows of the grid
 * \param nCols Number of cols of the grid
 */
void 
CloseGridEdge (uint32_t nRows, uint32_t nCols, PointToPointHelper p2p, PointToPointGridHelper grid);

/**
 * \brief print to the screen for notifying the progress when simulation is running
 *
 * Print a string at every 'interval' of simulation time, so that users can be
 * notified about the progress of the simulation
 *
 * \param interval The time interval between each notification
 */
void 
NotifyProgressEvery (Time interval);

/**
 * \brief Connect to CounterCalculator to every node of the grid topology
 *
 * \param counter A CounterCalculator to count the number of deflections occured
 * \param grid The grid topology
 * \param rows Number of rows of the grid
 * \param cols Number of columns of the grid
 */
void
TraceConnectForCountDeflection (Ptr<CounterCalculator<> > counter, PointToPointGridHelper grid, uint32_t rows, uint32_t cols);

/**
 * \brief Connect a counter to a flow monitor and count the total number of times forwarding is applied
 * to all packets, including the packets not delievered successfully
 *
 * \param counter The counter object to count the number of times of the forwarding
 * \param flowmon The flow monitor to which a trace sink connects for doing the counting
 */
void
TraceConnectForCountTxPacketTotalForwarding (Ptr<MinMaxAvgTotalCalculator<> > counter, Ptr<FlowMonitor> flowmon);

/**
 * \brief Connect a counter to a flow monitor and count the total number of times forwarding is applied
 * to only received packets, including the packets not delievered successfully
 *
 * \param counter The counter object to count the number of times of the forwarding
 * \param flowmon The flow monitor to which a trace sink connects for doing the counting
 */
void
TraceConnectForCountRxPacketTotalForwarding (Ptr<MinMaxAvgTotalCalculator<> > counter, Ptr<FlowMonitor> flowmon);

/**
 * \brief Connect a counter to a flow monitor and count total lifetime of all packets, including
 * the packets not delievered successfully
 *
 * \param counter The counter object to count the total lifetime
 * \param flowmon The flow monitor to which a trace sink connects for doing the counting
 */
void
TraceConnectForCountTotalPacketTimelife (Ptr<TimeMinMaxAvgTotalCalculator> counter, Ptr<FlowMonitor> flowmon);

/**
 * \brief Connect a counter to all net devices and count the total number bytes forwarded across a net device.
 * The bytes size includes all ppp header and ipv4 header
 *
 * \param counter A counter to count the number of bytes across every link
 * \param grid The grid topology
 * \param rows Number of rows of the grid
 * \param cols Number of columns of the grid
 */
void
TraceConnectForCountTotalBytesAcrossLinks (Ptr<PacketSizeMinMaxAvgTotalCalculator> counter, PointToPointGridHelper grid, uint32_t rows, uint32_t cols);

/**
 * \brief Count the number of packet sent over time by connecting a Histogram to a appropriate trace source in the flow monitor
 *
 * \param hist the histogram object with appropriate bin size set to some time interval
 * \param flowmon the flow montior to which the histogram is connected to
 */
void
TraceConnectCountPacketSentOverTime (Ptr<HistogramCalculator> hist, Ptr<FlowMonitor> flowmon);

/**
 * \brief Count the number of packet received over time by connecting a Histogram to a appropriate trace source in the flow monitor
 *
 * \param hist the histogram object with appropriate bin size set to some time interval
 * \param flowmon the flow montior to which the histogram is connected to
 */
void
TraceConnectCountPacketReceivedOverTime (Ptr<HistogramCalculator> hist, Ptr<FlowMonitor> flowmon);

/**
 * \brief Count the number of packet dropped over time by connecting a Histogram to a appropriate trace source in the flow monitor
 *
 * \param hist the histogram object with appropriate bin size set to some time interval
 * \param flowmon the flow montior to which the histogram is connected to
 */
void
TraceConnectCountPacketDroppedOverTime (Ptr<HistogramCalculator> hist, Ptr<FlowMonitor> flowmon);

/**
 * \brief Count the number of active packets leaving in the network over time
 *
 * \param out The output argument. It contains the counting results
 * \param tx Number of packets sent at each time interval
 * \param rx Number of packets received at each time interval
 * \param drop Number of packets dropped at each time interval
 */ 
void
CountNumActivePacketOverTime (Histogram* out, Histogram* tx, Histogram* rx, Histogram* drop);

/**
 * \brief calculate the most appropriate time interval to count the number of some events in each time interval over
 * the entire simulation period
 *
 * \param numPackets number of packets to generate of each traffic flow
 * \param dataRate data rate of each traffic flow
 * \param packetSize size of each packet in bytes
 * \param numIntervals at least this number of intervals needed for the entire simulation period
 * \return The time interval in secs
 */ 
double
GetTimeInterval (uint32_t numPackets, uint32_t packetSize, DataRate dataRate, uint32_t numIntervals);

class HistogramCalculator : public Histogram, public DataCalculator
{
public:
  HistogramCalculator ();
  HistogramCalculator (double binWidth);

  /// \brief Implements the virtual=0 method from DataCalculator
  virtual void Output(DataOutputCallback &callback) const;
  /// \brief This method suspends to work if DataCalculator is disabled
  virtual void AddValue (double value);
};

} // namespace eecityu
} // namespace ns3
